package com.formula.distribution.idempotent.aspect;

import com.formula.distribution.idempotent.annotation.ExtApiIdempotent;
import com.formula.distribution.idempotent.annotation.ExtApiToken;
import com.formula.distribution.idempotent.base.Constants;
import com.formula.distribution.idempotent.utils.RedisUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;

/**
 * API 幂等性AOP
 *
 * @author yang yang
 * @create 2018/12/10
 */
@Aspect
@Component
public class ExtApiIdempotentAspect {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private StringRedisTemplate redisTemplate;

//    @Pointcut("execution(public * com.formula..*.controller.*.*(..))")
//    public void extAspect() {
//    }

    @Pointcut("@annotation(com.formula.distribution.idempotent.annotation.ExtApiIdempotent)")
    public void extAspect() {
    }

    @Before("extAspect()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //1. 反射获取是否有使用ExtApiToken注解
        ExtApiToken extApiToken = signature.getMethod().getDeclaredAnnotation(ExtApiToken.class);
        //2. 非空时 在请求 header 中放入 token
        if (null != extApiToken) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String token = redisUtils.getToken(redisTemplate, extApiToken.value());
            attributes.getRequest().setAttribute("token", token);
        }
    }

    @Around("extAspect()")
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //1. 反射获取是否有使用ExtApiIdempotent注解
        ExtApiIdempotent extApiIdempotent = signature.getMethod().getDeclaredAnnotation(ExtApiIdempotent.class);
        //2. 为空时 直接执行代码
        if (null == extApiIdempotent) {
            return joinPoint.proceed();
        }

        //
        if (StringUtils.isEmpty(extApiIdempotent.value())) {
            response("参数异常");
            return null;
        }

        //用户信息中无token
        HttpServletRequest request = attributes.getRequest();

        String token = null;
        if (extApiIdempotent.value().equals(Constants.HEAD)) {
            token = request.getHeader("token");
        } else if (extApiIdempotent.value().equals(Constants.PARAM)) {
            token = request.getParameter("token");
        }

        if (StringUtils.isEmpty(token)) {
            response("参数错误");
            return null;
        }

        if (exitToken(token)) {
            response("请勿重复提交数据");
            return null;
        }
        return joinPoint.proceed();
    }

    /**
     * 校验token是否存在
     *
     * @param token
     * @return
     */
    private boolean exitToken(String token) {
        String value = redisTemplate.opsForValue().get(token);
        //判断redis中是否存在且是否存在值
        if (StringUtils.isEmpty(value)) {
            return false;
        }
        //移除该key
        redisTemplate.expire(token, 0, TimeUnit.MILLISECONDS);
        return true;
    }

    private void response(String msg) throws IOException {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = attributes.getResponse();
        response = attributes.getResponse();
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        writer.println(msg);
        writer.flush();
        writer.close();
    }
}